/*! MediaMatch v.2.0.3 - Testing css media queries in Javascript. Authors & copyright (c) 2013: WebLinc, David Knight. */

export default function() {
    'use strict';

    /* istanbul ignore else */
    if (window.matchMedia) {
        return window.matchMedia;
    } else {
        // Internal globals
        var win         = window,
            _doc        = win.document,
            _viewport   = _doc.documentElement,
            _queries    = [],
            _queryID    = 0,
            _type       = '',
            _features   = {},
                        // only screen
                        // only screen and
                        // not screen
                        // not screen and
                        // screen
                        // screen and
            _typeExpr   = /\s*(only|not)?\s*(screen|print|[a-z\-]+)\s*(and)?\s*/i,
                        // (-vendor-min-width: 300px)
                        // (min-width: 300px)
                        // (width: 300px)
                        // (width)
                        // (orientation: portrait|landscape)
            _mediaExpr  = /^\s*\(\s*(-[a-z]+-)?(min-|max-)?([a-z\-]+)\s*(:?\s*([0-9]+(\.[0-9]+)?|portrait|landscape)(px|em|dppx|dpcm|rem|%|in|cm|mm|ex|pt|pc|\/([0-9]+(\.[0-9]+)?))?)?\s*\)\s*$/,
            _timer      = 0,

            // Helper methods

            /*
                _matches
             */
            _matches = function (media) {
                // screen and (min-width: 400px), screen and (max-width: 500px)
                var mql         = (media.indexOf(',') !== -1 && media.split(',')) || [media],
                    mqIndex     = mql.length - 1,
                    mqLength    = mqIndex,
                    mq          = null,

                    // not screen, screen
                    negateType      = null,
                    negateTypeFound = '',
                    negateTypeIndex = 0,
                    negate          = false,
                    type            = '',

                    // (min-width: 400px), (min-width)
                    exprListStr = '',
                    exprList    = null,
                    exprIndex   = 0,
                    exprLength  = 0,
                    expr        = null,

                    prefix      = '',
                    length      = '',
                    unit        = '',
                    value       = '',
                    feature     = '',

                    match       = false;

                if (media === '') {
                    return true;
                }

                do {
                    mq          = mql[mqLength - mqIndex];
                    negate      = false;
                    negateType  = mq.match(_typeExpr);

                    if (negateType) {
                        negateTypeFound = negateType[0];
                        negateTypeIndex = negateType.index;
                    }

                    if (!negateType || ((mq.substring(0, negateTypeIndex).indexOf('(') === -1) && (negateTypeIndex || (!negateType[3] && negateTypeFound !== negateType.input)))) {
                        match = false;
                        continue;
                    }

                    exprListStr = mq;

                    negate = negateType[1] === 'not';

                    if (!negateTypeIndex) {
                        type        =  negateType[2];
                        exprListStr = mq.substring(negateTypeFound.length);
                    }

                    // Test media type
                    // Test type against this device or if 'all' or empty ''
                    match       = type === _type || type === 'all' || type === '';

                    exprList    = (exprListStr.indexOf(' and ') !== -1 && exprListStr.split(' and ')) || [exprListStr];
                    exprIndex   = exprList.length - 1;
                    exprLength  = exprIndex;

                    if (match && exprIndex >= 0 && exprListStr !== '') {
                        do {
                            expr = exprList[exprIndex].match(_mediaExpr);

                            if (!expr || !_features[expr[3]]) {
                                match = false;
                                break;
                            }

                            prefix  = expr[2];
                            length  = expr[5];
                            value   = length;
                            unit    = expr[7];
                            feature = _features[expr[3]];

                            // Convert unit types
                            if (unit) {
                                if (unit === 'px') {
                                    // If unit is px
                                    value = Number(length);
                                } else if (unit === 'em' || unit === 'rem') {
                                    // Convert relative length unit to pixels
                                    // Assumed base font size is 16px
                                    value = 16 * length;
                                } else if (expr[8]) {
                                    // Convert aspect ratio to decimal
                                    value = (length / expr[8]).toFixed(2);
                                } else if (unit === 'dppx') {
                                    // Convert resolution dppx unit to pixels
                                    value = length * 96;
                                } else if (unit === 'dpcm') {
                                    // Convert resolution dpcm unit to pixels
                                    value = length * 0.3937;
                                } else {
                                    // default
                                    value = Number(length);
                                }
                            }

                            // Test for prefix min or max
                            // Test value against feature
                            if (prefix === 'min-' && value) {
                                match = feature >= value;
                            } else if (prefix === 'max-' && value) {
                                match = feature <= value;
                            } else if (value) {
                                match = feature === value;
                            } else {
                                match = !!feature;
                            }

                            // If 'match' is false, break loop
                            // Continue main loop through query list
                            if (!match) {
                                break;
                            }
                        } while (exprIndex--);
                    }

                    // If match is true, break loop
                    // Once matched, no need to check other queries
                    if (match) {
                        break;
                    }
                } while (mqIndex--);

                return negate ? !match : match;
            },

            /*
                _setFeature
             */
            _setFeature = function () {
                // Sets properties of '_features' that change on resize and/or orientation.
                var w   = win.innerWidth || _viewport.clientWidth,
                    h   = win.innerHeight || _viewport.clientHeight,
                    dw  = win.screen.width,
                    dh  = win.screen.height,
                    c   = win.screen.colorDepth,
                    x   = win.devicePixelRatio;

                _features.width                     = w;
                _features.height                    = h;
                _features['aspect-ratio']           = (w / h).toFixed(2);
                _features['device-width']           = dw;
                _features['device-height']          = dh;
                _features['device-aspect-ratio']    = (dw / dh).toFixed(2);
                _features.color                     = c;
                _features['color-index']            = Math.pow(2, c);
                _features.orientation               = (h >= w ? 'portrait' : 'landscape');
                _features.resolution                = (x && x * 96) || win.screen.deviceXDPI || 96;
                _features['device-pixel-ratio']     = x || 1;
            },

            /*
                _watch
             */
            _watch = function () {
                clearTimeout(_timer);

                _timer = setTimeout(function () {
                    var query   = null,
                        qIndex  = _queryID - 1,
                        qLength = qIndex,
                        match   = false;

                    if (qIndex >= 0) {
                        _setFeature();

                        do {
                            query = _queries[qLength - qIndex];

                            if (query) {
                                match = _matches(query.mql.media);

                                if ((match && !query.mql.matches) || (!match && query.mql.matches)) {
                                    query.mql.matches = match;

                                    if (query.listeners) {
                                        for (var i = 0, il = query.listeners.length; i < il; i++) {
                                            if (query.listeners[i]) {
                                                query.listeners[i].call(win, query.mql);
                                            }
                                        }
                                    }
                                }
                            }
                        } while(qIndex--);
                    }


                }, 10);
            },

            /*
                _init
             */
            _init = function () {
                var head        = _doc.getElementsByTagName('head')[0],
                    style       = _doc.createElement('style'),
                    info        = null,
                    typeList    = ['screen', 'print', 'speech', 'projection', 'handheld', 'tv', 'braille', 'embossed', 'tty'],
                    typeIndex   = 0,
                    typeLength  = typeList.length,
                    cssText     = '#mediamatchjs { position: relative; z-index: 0; }',
                    eventPrefix = '',
                    addEvent    = win.addEventListener || (eventPrefix = 'on') && win.attachEvent;

                style.type  = 'text/css';
                style.id    = 'mediamatchjs';

                head.appendChild(style);

                // Must be placed after style is inserted into the DOM for IE
                info = (win.getComputedStyle && win.getComputedStyle(style)) || style.currentStyle;

                // Create media blocks to test for media type
                for ( ; typeIndex < typeLength; typeIndex++) {
                    cssText += '@media ' + typeList[typeIndex] + ' { #mediamatchjs { position: relative; z-index: ' + typeIndex + ' } }';
                }

                // Add rules to style element
                if (style.styleSheet) {
                    style.styleSheet.cssText = cssText;
                } else {
                    style.textContent = cssText;
                }

                // Get media type
                _type = typeList[(info.zIndex * 1) || 0];

                head.removeChild(style);

                _setFeature();

                // Set up listeners
                addEvent(eventPrefix + 'resize', _watch, false);
                addEvent(eventPrefix + 'orientationchange', _watch, false);
            };

        _init();

        /*
            A list of parsed media queries, ex. screen and (max-width: 400px), screen and (max-width: 800px)
        */
        return function (media) {
            var id  = _queryID,
                mql = {
                    matches         : false,
                    media           : media,
                    addListener     : function addListener(listener) {
                        _queries[id].listeners || (_queries[id].listeners = []);
                        listener && _queries[id].listeners.push(listener);
                    },
                    removeListener  : function removeListener(listener) {
                        var query   = _queries[id],
                            i       = 0,
                            il      = 0;

                        if (!query) {
                            return;
                        }

                        il = query.listeners.length;

                        for ( ; i < il; i++) {
                            if (query.listeners[i] === listener) {
                                query.listeners.splice(i, 1);
                            }
                        }
                    }
                };

            if (media === '') {
                mql.matches = true;
                return mql;
            }

            mql.matches = _matches(media);

            _queryID = _queries.push({
                mql         : mql,
                listeners   : null
            });

            return mql;
        };
    }
}
